#include "lista.h"
#include <iostream>
#include <sstream> // define el tipo ostringstream
#include <string>
#include <cassert> // define assert
#include <cstdlib>

using namespace std;

#define RUNTEST(t){ cerr << "Corriendo test " << #t << endl; t (); cerr << "..ok" << endl; }

template <typename T>
void asegurar(const Lista<T>& l, string s){
    ostringstream out;
    l.mostrarLista(out);
    string t = out.str();
    if(s != t){
        cerr << s << " != " << t << endl;
        assert(false);
    }
}

template <typename T>
void randList(Lista<T>& l, int n){
    while(n--) {
		l.agregarAtras(rand()%1000);
	}
}

/******************************************************************************************/
/*                               tests sobre las funciones                                */
/******************************************************************************************/

void testCopia(){
    Lista<int> l;
    Lista< Lista<int> > ll;
    randList(l,10);
    Lista<int> l2(l);
    assert(l==l2);
    l.agregar(0);
    assert(l!=l2);
    ll.agregar(l2);
    ll.agregar(l);
    Lista< Lista<int> > ll2(ll);
    assert(ll==ll2);
}

void testAgregar(){
        Lista<int> l;
        Lista< Lista<int> > ll;
        l.agregar(0);
        asegurar(l,"[0]");
        l.agregar(8);
        asegurar(l,"[8,0]");
        l.agregar(-15);
        asegurar(l,"[-15,8,0]");
        ll.agregar(l);
        l.agregar(52);
        asegurar(l,"[52,-15,8,0]");
        l.agregar(487);
        asegurar(l,"[487,52,-15,8,0]");
        ll.agregar(l);
        l.agregar(-1);
        asegurar(l,"[-1,487,52,-15,8,0]");
        ll.agregar(l);
        asegurar(ll,"[[-1,487,52,-15,8,0],[487,52,-15,8,0],[-15,8,0]]");
}

void testEsVacia(){
    Lista<int> l;
    Lista< Lista<int> > ll;
    Lista<int> lc;
    assert(l.esVacia());
    assert(ll.esVacia());
    assert(lc.esVacia());
    asegurar(l,"[]");
    asegurar(ll,"[]");
    asegurar(lc,"[]");
}

void testPrimero(){
    Lista<int> l;
    Lista< Lista<int> > ll;
    l.agregar(1);
    assert(l.primero()==1);
    l.agregarAtras(2);
    assert(l.primero()==1);
    l.agregar(0);
    assert(l.primero()==0);
    ll.agregarAtras(l);
    assert(ll.primero()==l);
}

void testAgregarAtras(){
        Lista<int> l;
        Lista< Lista<int> > ll;
        l.agregarAtras(0);
        asegurar(l,"[0]");
        l.agregarAtras(8);
        asegurar(l,"[0,8]");
        l.agregarAtras(-15);
        asegurar(l,"[0,8,-15]");
        ll.agregarAtras(l);
        l.agregarAtras(52);
        asegurar(l,"[0,8,-15,52]");
        l.agregarAtras(487);
        asegurar(l,"[0,8,-15,52,487]");
        ll.agregarAtras(l);
        l.agregarAtras(-1);
        asegurar(l,"[0,8,-15,52,487,-1]");
        ll.agregarAtras(l);
        asegurar(ll,"[[0,8,-15],[0,8,-15,52,487],[0,8,-15,52,487,-1]]");
}

void testLongitud(){
    Lista<int> l;
    Lista< Lista<int> > ll;
    assert(l.longitud()==0);
    randList(l,56);
    assert(l.longitud()==56);
    l.agregar(-1);
    assert(l.longitud()==57);
    for(int i=0;i<50;i++){
        assert(ll.longitud()==i);
        assert(ll.longitud()==i);
        l.agregar(rand()%1000);
        ll.agregar(l);
    }
}

void testEsta(){
    Lista<int> l;
    l.agregar(0);
    l.agregarAtras(1);
    assert(l.esta(0));
    assert(l.esta(1));
    assert(!l.esta(2));
    Lista< Lista<int> > ll;
    ll.agregarAtras(l);
    assert(ll.esta(l));
}

void testIesimo(){
    Lista<int> l;
    l.agregar(2);
    l.agregar(1);
    l.agregarAtras(3);
    l.agregar(0);
    l.agregarAtras(4);
    for(int i=0;i<5;i++){
        assert(l.iesimo(i)==i);
    }
}

void testIterador(){
    Lista<int> l;
    l.agregarAtras(5);
    l.agregar(6);
    l.agregarAtras(3);
    l.agregarAtras(3);
    l.agregar(1);
    l.agregarAtras(9);
    l.agregarAtras(25);
    l.agregarAtras(7);
    asegurar(l,"[1,6,5,3,3,9,25,7]");
    Lista<int> l2;
    Lista<int>::IterLista it = l.crearIt();
    while(it.tieneProximo()){
        l2.agregarAtras(it.actual());
        it.avanzar();
    }
    assert(l==l2);
    Lista<int>::IterLista it2 = l2.crearIt();
    while(it2.tieneProximo()){
        it2.eliminar();
    }
    assert(l2.esVacia());
    assert(7==l.crearItAlUlt().actual());
}

void testIterador_const(){
    Lista<int> l;
    l.agregarAtras(5);
    l.agregar(6);
    l.agregarAtras(3);
    l.agregarAtras(3);
    l.agregar(1);
    l.agregarAtras(9);
    l.agregarAtras(25);
    l.agregarAtras(7);
    Lista<int> l2;
    Lista<int>::IterLista_const it_c = l.crearIt_const();
    while(it_c.tieneProximo()){
        l2.agregarAtras(it_c.actual());
        it_c.avanzar();
    }
    assert(l==l2);
    assert(l==it_c.lista());
}

main(){
    RUNTEST(testEsVacia);
    RUNTEST(testAgregar);
    RUNTEST(testAgregarAtras);
    RUNTEST(testCopia);
    RUNTEST(testPrimero);
    RUNTEST(testLongitud);
    RUNTEST(testEsta);
    RUNTEST(testIesimo);
    RUNTEST(testIterador);
    RUNTEST(testIterador_const);
    return 0;
}
